#!/bin/bash
#
# Core functions that implement the "main" logic behind Pulley.
#
# Author:   Dr. Mike Murphy (mmurphy2@coastal.edu)
# Revision: 18 November 2013
#
#   Copyright 2011-2013 Coastal Carolina University
#   Copyright 2007-2008 Clemson University
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#
# This material is based upon work supported under a National Science
# Foundation Graduate Research Fellowship. Any opinions, findings,
# conclusions or recommendations expressed in this publication are those of
# the author(s) and do not necessarily reflect the views of the National
# Science Foundation.


# Main Pulley version is set here
declare PULLEY_VERSION=0.92

# Cleanup routines: store function names
declare -a -g PK_CLEANUP_HOOKS


## p_init
##
## Initializes the kernel environment by loading the necessary core
## modules, followed by any site configuration files. Returns 0 on
## success, non-zero on failure. Stops the initialization process upon
## the first failure.
##
function p_init() {
	local status=0
	local platform=
	
	# IMPORTANT: Everything inside this function is run BEFORE the main
	# lock is acquired. Thus, these operations should not cause peristent
	# state changes.
	
	# Load all core modules
	for lib in $(ls "${P_LIBDIR}/core"/*.module); do
		# Stop on error
		if ((status == 0)); then
			. "${lib}"
			status=$?
		fi
	done
	
	# Load Pulley configuration files
	for cfg in "${P_CONFIGFILES[@]}"; do
		if ((status == 0)); then
			. "${cfg}"
			status=$?
		fi
	done
	
	return ${status}
}


## register_cleanup_hook <function name>
##
## Registers a function named <function name> to be called by the cleanup
## handler at cleanup time. Always returns 0.
function register_cleanup_hook() {
	PK_CLEANUP_HOOKS+=("$1")
	return 0
}


## p_cleanup
##
## Executes cleanup routines provided by various core modules, as well as
## any cleanup tasks required by the kernel. Returns 0 if all hooks return 0,
## otherwise returns the non-zero exit status of the first hook that
## encounters an error. Continues executing remaining hooks on error.
##
function p_cleanup() {
	local status=0
	
	for hook in "${PK_CLEANUP_HOOKS[@]}"; do
		"${hook}"
		status=$(merge_status ${status} $?)
	done
	
	return ${status}
}


## p_main <command> [args]
##
## The main Pulley subroutine. This function initiates all execution once
## control has been transferred from the bootstrapper to the kernel.
##
## <command>     Name of the Pulley command to execute
## [args]        Any arguments to the command
##
function p_main() {
	local status=0
	local startclock
	local endclock
	local runtime
	
	p_init
	status=$?
	
	if ((status == 0)); then
		lock_pulley
		status=$?
		
		if ((status == 0)); then
			startclock=$(t_timestamp)
			p_log_preamble >> "${P_LOGFILE}"
			
			# Detect and set initial platform context
			p_platform_detect
			status=$?
			
			# Update UUID
			if ((status == 0)); then
				p_uuid_update
				status=$?
			fi
			
			# Off to run the command
			if ((status == 0)); then
				run_command "$@" 2>&1 | tee -a "${P_LOGFILE}"
				status=$?
			fi
		
			endclock=$(t_timestamp)
			runtime=$((endclock - startclock))
		
			p_log_epilog ${runtime} >> "${P_LOGFILE}"
		else
			echo "Cannot acquire the Pulley lock: exiting"
		fi
	
		unlock_pulley
		status=$(merge_status ${status} $?)
	fi
	
	p_cleanup
	status=$(merge_status ${status} $?)
	
	return ${status}
}
